package com.my.study.dev;

import cn.hutool.core.net.URLEncoder;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpResponse;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONUtil;
import com.my.study.bean.User;
import com.my.study.date.DateTimeUtils;
import com.my.study.json.JSONUtils;
import com.my.study.utils.AESUtils;
import org.apache.commons.compress.utils.Lists;
import org.junit.Test;

import java.io.UnsupportedEncodingException;
import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Objects;
import java.util.Random;
import java.util.Set;
import java.util.StringJoiner;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @author: yidujun
 * @create: 2021/03/25 17:16
 */
public class DevTest {
    
    
    @Test
    public void random() {
        BigDecimal totalAmount = new BigDecimal("20");
        int totalCount = 10;
        List<BigDecimal> result = new ArrayList<>();
        BigDecimal restAmount = totalAmount; // 剩余未分配金额
        int restCount = totalCount; // 剩余未分配红包个数
        Random random = new Random();

        // 随机红包序列号列表，总和为1
        List<Double> list = new ArrayList<>(totalCount);
        double sum = 0;
        for (int i = 0; i < totalCount; i++) {
            double r = random.nextDouble() * (restCount / (double) totalCount * 2);
            list.add(r);
            sum += r;
        }
        for (int i = 0; i < totalCount; i++) {
            BigDecimal amount = totalAmount.multiply(BigDecimal.valueOf(list.get(i) / sum))
                    .setScale(2, BigDecimal.ROUND_HALF_UP);
            // 如果剩余金额不足或只剩最后一个红包，则把剩余金额全部分配给当前红包
            if (restAmount.compareTo(amount) < 0 || restCount == 1) {
                amount = restAmount;
            }
            result.add(amount);
            restAmount = restAmount.subtract(amount);
            restCount--;
        }
        System.out.println(result);
    }
    

    @Test
    public void testBigDecimal() {
        // 1.add
        BigDecimal v1 = new BigDecimal("0.041883");
        BigDecimal v2 = new BigDecimal("0.090551");
        BigDecimal v3 = new BigDecimal("0.867566");
        double value = v1.add(v2).add(v3).doubleValue();
        System.out.println(value);

        // 2.0操作
        String total = "100";
        String stock = "0";
        String bond = "0";
        double value1 = new BigDecimal(total).subtract(new BigDecimal(stock)).subtract(new BigDecimal(bond)).doubleValue();
        System.out.println(value1);

        double a = 1d;
        double b = 1.2134;
        // 首
        BigDecimal headValue = BigDecimal.valueOf(a);
        // 尾
        BigDecimal tailValue = BigDecimal.valueOf(b);
        double netValue = tailValue.divide(headValue, 6, BigDecimal.ROUND_HALF_UP).subtract(new BigDecimal(1)).doubleValue();
        System.out.println(netValue);

    }

    /**
     * 测试if-else
     */
    @Test
    public void testIfElse() {
        int c = 2;
        if (c % 2 == 0) {
            if (c == 0) {
                System.out.println(1);
                return;
            }
        } else if (c == 1) {
            System.out.println(2);
            return;
        }
        System.out.println(3);
        return;
    }

    @Test
    public void testCalendar() {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(new Date());
        int nowMonth = calendar.get(Calendar.MONDAY);
        int nowYear = calendar.get(Calendar.YEAR);
        System.out.println(nowYear + "/" + (nowMonth + 1));
        System.out.println(calendar.getTime());

        calendar.add(Calendar.MONDAY, -3);
        int updateMonth = calendar.get(Calendar.MONDAY);
        int updateYear = calendar.get(Calendar.YEAR);
        System.out.println(updateYear + "/" + (updateMonth + 1));
        System.out.println(calendar.getTime());

        calendar.add(Calendar.MONDAY, -3);
        int updateMonth1 = calendar.get(Calendar.MONDAY);
        int updateYear1 = calendar.get(Calendar.YEAR);
        System.out.println(updateYear1 + "/" + (updateMonth1 + 1));
        System.out.println(calendar.getTime());


        calendar.set(1970, 1, 1);
        calendar.add(Calendar.MONDAY, -3);
        System.out.println(calendar.getTime());
    }

    @Test
    public void sortString() {
        String str = "002127.SZ,002013.SZ,000513.SZ,000961.SZ,000858.SZ,000547.SZ,002271.SZ,601318.SH,600519.SH,600048.SH";
        String[] split = str.split(",");
        String s = Arrays.stream(split).sorted().collect(Collectors.joining(","));
        System.out.println(s);
    }

    @Test
    public void testFile() {
        User user = new User();
        user.setName("ss");

        try {
            Field name = User.class.getDeclaredField("name");
            name.setAccessible(true);
            String s = (String) name.get(user);
            System.out.println(s);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Test
    public void StringTest() {
        String str = "jkdjjs";
        int length = str.length();
        System.out.println("字符串长度: " + length);
        int last = str.lastIndexOf("]", length);
        int first = str.lastIndexOf("[", length);
        System.out.println(first + "   " + last);

        String indexName = str.substring(0, first);
        System.out.println(indexName);
        String unit = str.substring(first + 1, last);
        System.out.println(unit);
    }

    private String matchNumber = "-?[0-9]+(\\.[0-9]+)?";

    @Test
    public void long2string() {
        String id = "n1";
        System.out.println(isNumber(id));
    }

    public boolean isNumber(String str) {
        Pattern compile = Pattern.compile(matchNumber);
        String bigStr;
        try {
            bigStr = new BigDecimal(str).toString();
        } catch (Exception e) {
            return false;
        }
        Matcher matcher = compile.matcher(bigStr);
        return matcher.matches();
    }

    @Test
    public void testNullInteger() {
        Integer num = null;

        if (Objects.isNull(num) || num <= 0) {
            num = 455;
        }
        System.out.println(num);
    }

    @Test
    public void testInt() {
        Integer i = new Integer(0);
        addAndGet(i);
        System.out.println(i);
    }

    private void addAndGet(Integer i) {
        for (int j = 0; j < 100; j++) {
            i++;
        }
    }

    @Test
    public void testStreamCount() {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            list.add(i + 1);
        }
        long count = list.stream().filter(l -> l > 10).count();
        System.out.println((int) count + 1);
    }

//    public <T> void handleList(List<T> list,
//                               Supplier<Integer> batchNum,
//                               Supplier<? extends BaseDao> dao,
//                               BiFunction<List<T>, Map<String, Object>, List<T>> function,
//                               BiPredicate<T, T> predicate) {
//
//        Integer num = batchNum.get();
//        if (Objects.isNull(num) || num <= 0) {
//            num = 100;
//        }
//        int capacity = Math.min(num, 500);
//        Map<String, Object> params = new HashMap<>();
//
//        Lists.partition(list, capacity).forEach(data -> {
//            List<T> oldData = function.apply(data, params);
//            BaseDao baseDao = dao.get();
//
//            if (CollectionUtils.isEmpty(oldData)) {
//                baseDao.insert(data);
//            }
//            else if (data.size() == oldData.size()) {
//                baseDao.update(data);
//            }
//            else {
//                List<T> update = data.stream().filter(f -> {
//                    for (T old : oldData) {
//                        if (predicate.test(f, old)) {
//                            return true;
//                        }
//                    }
//                    return false;
//                }).collect(Collectors.toList());
//                baseDao.update(update);
//
//                List<T> insert = data.stream().filter(f -> {
//                    for (T old : update) {
//                        if (predicate.test(f, old)) {
//                            return false;
//                        }
//                    }
//                    return true;
//                }).collect(Collectors.toList());
//                baseDao.insert(insert);
//            }
//        });
//    }

    static final ThreadLocal<Boolean> IS = new ThreadLocal<>();

    @Test
    public void test() {
//        IS.set(true);
        try {
            Boolean aBoolean = IS.get();
            if (aBoolean) {
                System.out.println("----");
                throw new RuntimeException("---");
            }
        } finally {
            System.out.println("over...");
            IS.remove();
            Boolean aBoolean = IS.get();
            System.out.println(aBoolean);
        }
    }

    @Test
    public void sortListContainNull() {
        List<User> list = new ArrayList<>();
        list.add(new User(1L, "102.944"));
        list.add(new User(2L, "32"));
        list.add(new User(3L, "390"));
        list.add(new User(5L, ""));
        list.add(new User(4L, null));

        List<User> collect = list.stream().filter(f -> f.getName() != null && !"".equals(f.getName()))
                .sorted(Comparator.comparing(r -> Double.parseDouble(((User) r).getName())).reversed()).collect(Collectors.toList());

        List<User> collect1 = list.stream().filter(f -> f.getName() == null || "".equals(f.getName())).sorted(Comparator.comparing(User::getId)).collect(Collectors.toList());

        collect.addAll(collect1);

        System.out.println(collect);
    }

    @Test
    public void term() {
        String str = "0000";
        System.out.println(trimBothEndsChars(str, "\\*"));
    }

    public String trimBothEndsChars(String srcStr, String splitter) {
        String regex = "^" + splitter + "*|" + splitter + "*$";
        return srcStr.replaceAll(regex, "");
    }

    @Test
    public void testEq() {
        int a = -2;
        int b = -2;
        int c = -2;
        int d = 2;

        if ((a & b & c & d) == a) {
            System.out.println(a);
        } else {
            System.out.println("-1");
        }
    }

    @Test
    public void testEx() {
        try {
            try {
                System.out.println(500 << 3);
                int i = 1 / 0;
            } catch (Exception e) {
                System.out.println("doEx ....");
//                throw new RuntimeException();
            } finally {

            }
        } catch (Exception e) {
            System.out.println("testEx ....");
        } finally {
            System.out.println("testEx final ....");
        }
    }

    public void doEx() throws Exception {
        try {
            int i = 1 / 0;
        } catch (Exception e) {
            System.out.println("doEx ....");
        }
    }

    @Test
    public void strToDate() {
        String str = "2021-01-20 12:33:09";
        Date date = null;
        try {
            date = DateTimeUtils.stringToDate(str, "yyyy-MM-dd HH:mm:ss", ZoneId.of("Asia/Shanghai"));
        } catch (ParseException e) {
            e.printStackTrace();
        }
        System.out.println(date);
//        TemporalAccessor parse = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss").parse(str);
//        System.out.println(parse);
//        Instant from = Instant.from(parse);
//        System.out.println(from);
//
//        Date from1 = Date.from(from);
//        System.out.println(from1);

    }


    @Test
    public void cyclicBarrierTest() {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(5 + 1);

        ExecutorService executorService = Executors.newFixedThreadPool(8);

        System.out.println("cyclicBarrierTest..........start");
        for (int i = 0; i < 2; i++) {
            CyclicBarrierRunnable cyclicBarrierRunnable = new CyclicBarrierRunnable(cyclicBarrier, i);
            executorService.execute(cyclicBarrierRunnable);
        }
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        System.out.println("cyclicBarrierTest..........end");

    }

    class CyclicBarrierRunnable implements Runnable {
        private CyclicBarrier cyclicBarrier;
        private int i;

        public CyclicBarrierRunnable(CyclicBarrier cyclicBarrier, int i) {
            this.cyclicBarrier = cyclicBarrier;
            this.i = i;
        }

        @Override
        public void run() {
            System.out.println("CyclicBarrierRunnable...." + i);
            try {
                cyclicBarrier.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    @Test
    public void countDownLatchTest() {
        AtomicInteger id = new AtomicInteger(0);
        ExecutorService executorService = Executors.newFixedThreadPool(8,
                r -> new Thread(r, "test_" + id.getAndIncrement()));
        CountDownLatch countDownLatch = new CountDownLatch(2);
        for (int i = 0; i < 2; i++) {
            CountDownLatchRunnable runnable = new CountDownLatchRunnable(countDownLatch);
            executorService.execute(runnable);
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            System.out.println("被打断....被打断...");
            Thread.currentThread().interrupt();
            e.printStackTrace();
        }
    }

    class CountDownLatchRunnable implements Runnable {

        CountDownLatch countDownLatch;

        public CountDownLatchRunnable(CountDownLatch countDownLatch) {
            this.countDownLatch = countDownLatch;
        }

        @Override
        public void run() {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName() + "DODODOODOD");
            countDownLatch.countDown();
        }
    }

    @Test
    public void testDeleteAllHTMLTag() {
        String str = "<p> dfkjokjp</p> <script> sds </script> <a href='dfjkdfjkdjk'>dfdslkfjl阿胶呢</a> <ryrurk>";
        String s = deleteAllHTMLTag(str);
        System.out.println(s);
    }

    /**
     * 删除所有的HTML标签
     *
     * @param source 需要进行除HTML的文本
     * @return
     */
    public String deleteAllHTMLTag(String source) {

        if (source == null) {
            return "";
        }

        String s = source;
        /** 删除普通标签  */
        s = s.replaceAll("<(S*?)[^>]*>.*?|<.*? />", "");
        /** 删除转义字符 */
        s = s.replaceAll("&.{2,6}?;", "");
        return s;
    }

    @Test
    public void testMapRemove() {
        Map<String, String> map = new HashMap<>();
        String remove = map.remove("a");
        // null
        System.out.println(remove);
    }

    @Test
    public void testRequest() throws UnsupportedEncodingException {
        String str = "%E8%A5%BF%E5%8D%97%E8%AF%81%E5%88%B8";
        String s = URLDecoder.decode(str, "UTF-8");
        System.out.println(s);
    }

    @Test
    public void testListClear() {
        List<Long> list = new ArrayList<>();
        list.add(1L);
        list.add(2L);
        System.out.println(list.size());

        list.clear();
        System.out.println(list.size());

        list.get(list.size());

        Long aLong = list.stream().skip(list.size() - 1).findFirst().orElse(null);
        System.out.println(aLong);
    }

    @Test
    public void testFin() {

        try {
            int i = 1 / 0;
//            try {
//                System.out.println("1~~~");
//                throw new RuntimeException("hhhhhhhhh");
//            } catch (Exception e) {
//                System.out.println("1 ~~~~~~~~~~~~catch");
//                if (e instanceof RuntimeException) {
//                    System.out.println(e.getMessage());
//                }
//            } finally {
//                System.out.println("1 ~~~~ finally");
//            }
        } catch (Exception e) {
            System.out.println("2 ~~~~~~~~~~~~catch");
//            throw e;
        } finally {
            System.out.println("2 ~~~~ finally");
        }
        System.out.println("======");
    }

    /**
     * 测试Tomcat中TaskQueue队列
     */
    @Test
    public void testExtend() {
        TaskQueue taskQueue = new TaskQueue();
        taskQueue.method01();
        taskQueue.method02();


        String b = "96.50";

        System.out.println(new BigDecimal("100").subtract(new BigDecimal(b)).toString());

        System.out.println(6 >> 32);
        System.out.println(6 >> 1);

    }

    @Test
    public void testStringJoiner() {
        StringJoiner joiner = new StringJoiner(",", "[", "]");
        joiner.add(format("医疗服务", "生物制品"));
        joiner.add(format("生猪养殖", "猪肉"));
        System.out.println(joiner.toString());
    }

    private String format(String graphName, String tag) {
        // 校验参数

//        ReportTagFormat tagFormat = new ReportTagFormat(graphName, tag);
//        return tagFormat.format();
//
        String format = "{\"graphName\":" + "\"%s\"," + "\"tag\":" + "\"%s\"" + "}";
        return String.format(format, graphName, tag);
    }

    public class ReportTagFormat {
        private String graphName;
        private String tag;

        public ReportTagFormat() {
        }

        public ReportTagFormat(String graphName, String tag) {
            this.graphName = graphName;
            this.tag = tag;
        }

        public String format() {
            return "{" +
                    "\"graphName\":" + "\"" + graphName + '\"' +
                    ",\"tag\":" + "\"" + tag + '\"' +
                    '}';
        }
    }

    public static final int COUNT = 1000000;

    @Test
    public void KMPTest() {
        String target = "A 股策略聚焦—风格平衡开启，切换尚需时间投资策略｜近期成长制造板块剧烈波动明显增多，风格平衡的过程已经开启，过度\n" +
                "解读政策并传播焦虑的问题正在被纠偏，市场也已经充分反映了经济预期的下修，\n" +
                "预计 8 月风格平衡的过程将持续，不过风格的切换可能在三季度末才会发生。首先，\n" +
                "成长制造板块持仓拥挤，板块内部分歧也在加大，消费医药类基金资金流出压力缓\n" +
                "解，下周新能源板块内部存在较大分流压力，板块层面呈现此消彼长态势，指数层\n" +
                "面也显示极端分化有所缓解。其次，近期市场焦虑犹存，片面的评判行业和影射政\n" +
                "策已经造成巨大负面影响，而国家的产业政策目标和路径明晰，错误夸大或引导政\n" +
                "策认知的行为正在被纠偏。最后，尽管 7 月经济、金融数据预计将有所转弱，但投\n" +
                "资者的中、微观感知领先于经济数据变化，市场已提前反映经济偏弱预期，不过未\n" +
                "来政策的调整和经济的企稳都需要时间，风格的均衡转向风格的切换尚需时间。我\n" +
                "们认为市场风格平衡的过程已经在发生，建议成长制造和价值消费保持均衡配置，\n" +
                "在成长板块里从高位赛道转向相对低位的赛道，并且逐步布局部分高景气的消费和\n" +
                "医药细分行业。全球科技产业 2021 年度展望 2—国内云基础设施（IaaS+PaaS）市场：进入\n" +
                "发展新周期，关注下半年周期性机会前瞻研究｜目前国内云计算市场已度过早期探索阶段，正进入新一轮发展周期，上\n" +
                "云主力从泛互联网向传统中大型企业迁移。同时考虑到国内传统行业相对偏弱的数\n" +
                "字化水平，需要云厂商在项目交付、渠道市场、产品完整性等层面持续发力，国内\n" +
                "云市场格局中期仍存在局部调整可能，亦吸引字节等新厂商进入。高速成长的市场、\n" +
                "突出的规模效应、不断优化的产品结构等料将推动一线云厂商强者恒强，建议长期\n" +
                "重点关注阿里云、腾讯云、华为云、百度云等头部厂商。我们亦看好下半年国内云\n" +
                "厂商 capex 预计反转会带来的 IDC、数据中心硬件（量+价）周期性投资机会，以\n" +
                "及北美云厂商 400G 技术周期下的高速光capex模块机会，建议重点关注万国数据、秦淮\n" +
                "数据、数据港、世纪互联、紫光股份、浪潮信息、中际旭创、新易盛等。2021 年 7 月美国非农数据点评—非农如期改善，Taper 渐行渐近宏观经济｜7 月美国非农就业数据如期改善，就业意愿低的问题已出现积极变化，\n" +
                "符合我们预期。Delta 病毒对美国经济和就业的冲击有限，往后失业救济金退坡会\n" +
                "进一步提升就业意愿。未来几个月美国就业有望加速修复，美联储 Taper 预期也将\n" +
                "渐行渐近。11111";
        String pattern = "capex";
        Instant start = Instant.now();
        for (int i = 0; i < COUNT; i++) {
            int kmp = matcherWithKMP(target, pattern);
        }
        System.out.println("cost = " + Duration.between(start, Instant.now()).toMillis() + "ms");

    }

    public int matcherWithKMP(String target, String pattern) {
        int[] next = new int[pattern.length()];
        initNext(pattern, next);
        int targetLen = target.length();
        int patternLen = pattern.length();
        int count = 1;
        for (int i = 0, j = 0; i <= targetLen; ) {
            if (j == patternLen || i == targetLen) {
                return count != patternLen ? -1 : i - j;
            }
            char t = target.charAt(i);
            char p = pattern.charAt(j);
            boolean eq = isLetter(t) && isLetter(p) ? equalsIgnoreCaseOfChar(t, p) : t == p;
            if (j == 0 || eq) {
                if (j != 0) {
                    count++;
                } else {
                    count = 1;
                }
                i++;
                j++;
            } else {
                j = next[j];
                count = 1;
            }
        }
        return -1;
    }

    private void initNext(String pattern, int[] next) {
        next[0] = 0;

        for (int i = 1, j = 0; i < pattern.length(); ) {
            if (j == 0 || pattern.charAt(i - 1) == pattern.charAt(j - 1)) {
                next[i] = j;
                i++;
                j++;
            } else {
                j = next[j];
            }
        }
    }

    private boolean isLetter(char c) {
        return c >= 65 && c <= 90 || c >= 97 && c <= 122;
    }

    private boolean equalsIgnoreCaseOfChar(char c1, char c2) {
        String s1 = String.valueOf(c1);
        String s2 = String.valueOf(c2);
        return s1.equalsIgnoreCase(s2);
    }

//    @Test
//    public void hanlpTest() {
//        String target = "A 股策略聚焦—风格平衡开启，切换尚需时间投资策略｜近期成长制造板块剧烈波动明显增多，风格平衡的过程已经开启，过度\n" +
//                "解读政策并传播焦虑的问题正在被纠偏，市场也已经充分反映了经济预期的下修，\n" +
//                "预计 8 月风格平衡的过程将持续，不过风格的切换可能在三季度末才会发生。首先，\n" +
//                "成长制造板块持仓拥挤，板块内部分歧也在加大，消费医药类基金资金流出压力缓\n" +
//                "解，下周新能源板块内部存在较大分流压力，板块层面呈现此消彼长态势，指数层\n" +
//                "面也显示极端分化有所缓解。其次，近期市场焦虑犹存，片面的评判行业和影射政\n" +
//                "策已经造成巨大负面影响，而国家的产业政策目标和路径明晰，错误夸大或引导政\n" +
//                "策认知的行为正在被纠偏。最后，尽管 7 月经济、金融数据预计将有所转弱，但投\n" +
//                "资者的中、微观感知领先于经济数据变化，市场已提前反映经济偏弱预期，不过未\n" +
//                "来政策的调整和经济的企稳都需要时间，风格的均衡转向风格的切换尚需时间。我\n" +
//                "们认为市场风格平衡的过程已经在发生，建议成长制造和价值消费保持均衡配置，\n" +
//                "在成长板块里从高位赛道转向相对低位的赛道，并且逐步布局部分高景气的消费和\n" +
//                "医药细分行业。全球科技产业 2021 年度展望 2—国内云基础设施（IaaS+PaaS）市场：进入\n" +
//                "发展新周期，关注下半年周期性机会前瞻研究｜目前国内云计算市场已度过早期探索阶段，正进入新一轮发展周期，上\n" +
//                "云主力从泛互联网向传统中大型企业迁移。同时考虑到国内传统行业相对偏弱的数\n" +
//                "字化水平，需要云厂商在项目交付、渠道市场、产品完整性等层面持续发力，国内\n" +
//                "云市场格局中期仍存在局部调整可能，亦吸引字节等新厂商进入。高速成长的市场、\n" +
//                "突出的规模效应、不断优化的产品结构等料将推动一线云厂商强者恒强，建议长期\n" +
//                "重点关注阿里云、腾讯云、华为云、百度云等头部厂商。我们亦看好下半年国内云\n" +
//                "厂商 capex 预计反转会带来的 IDC、数据中心硬件（量+价）周期性投资机会，以\n" +
//                "及北美云厂商 400G 技术周期下的高速光模块机会，建议重点关注万国数据、秦淮" +
//                "数据、数据港、世纪互联、紫光股份、浪潮信息、中际旭创、新易盛等。2021 年 7 月美国非农数据点评—非农如期改善，Taper 渐行渐近宏观经济｜7 月美国非农就业数据如期改善，就业意愿低的问题已出现积极变化，\n" +
//                "符合我们预期。Delta 病毒对美国经济和就业的冲击有限，往后失业救济金退坡会\n" +
//                "进一步提升就业意愿。未来几个月美国就业有望加速修复，美联储 Taper 预期也将\n" +
//                "渐行渐近。动物保健";
//
////        MyCustomDictionary.add("动物保健");
////        MySpeedTokenizer tokenizer = new MySpeedTokenizer(MyCustomDictionary.getDat());
//
////        CustomDictionary.add("动物保健");
////        CustomDictionary.add("开发");
////        CustomDictionary.add("idc");
//        CustomDictionary.add("秦淮数据");
//        Instant start = Instant.now();
//        List<Term> sentence = HanLP.newSegment()
//                .enableCustomDictionaryForcing(true)
//                .enableMultithreading(true)
//                .seg(target);
////        List<Term> sentence = SpeedTokenizer.segment(target);
//        for (int i = 0; i < COUNT; i++) {
//            for (Term terms : sentence) {
//                System.out.println(terms);
//                if (terms.word.equalsIgnoreCase("秦淮数据")) {
////                    System.out.println("匹配成功！");
//                } else {
//
//                }
//            }
//        }
//        System.out.println("cost = " + Duration.between(start, Instant.now()).toMillis() + "ms");
//    }


    @Test
    public void SundayTest() {
        String target = "A 股策略聚焦—风格平衡开启，切换尚需时间投资策略｜近期成长制造板块剧烈波动明显增多，风格平衡的过程已经开启，过度\n" +
                "解读政策并传播焦虑的问题正在被纠偏，市场也已经充分反映了经济预期的下修，\n" +
                "预计 8 月风格平衡的过程将持续，不过风格的切换可能在三季度末才会发生。首先，\n" +
                "成长制造板块持仓拥挤，板块内部分歧也在加大，消费医药类基金资金流出压力缓\n" +
                "解，下周新能源板块内部存在较大分流压力，板块层面呈现此消彼长态势，指数层\n" +
                "面也显示极端分化有所缓解。其次，近期市场焦虑犹存，片面的评判行业和影射政\n" +
                "策已经造成巨大负面影响，而国家的产业政策目标和路径明晰，错误夸大或引导政\n" +
                "策认知的行为正在被纠偏。最后，尽管 7 月经济、金融数据预计将有所转弱，但投\n" +
                "资者的中、微观感知领先于经济数据变化，市场已提前反映经济偏弱预期，不过未\n" +
                "来政策的调整和经济的企稳都需要时间，风格的均衡转向风格的切换尚需时间。我\n" +
                "们认为市场风格平衡的过程已经在发生，建议成长制造和价值消费保持均衡配置，\n" +
                "在成长板块里从高位赛道转向相对低位的赛道，并且逐步布局部分高景气的消费和\n" +
                "医药细分行业。全球科技产业 2021 年度展望 2—国内云基础设施（IaaS+PaaS）市场：进入\n" +
                "发展新周期，关注下半年周期性机会前瞻研究｜目前国内云计算市场已度过早期探索阶段，正进入新一轮发展周期，上\n" +
                "云主力从泛互联网向传统中大型企业迁移。同时考虑到国内传统行业相对偏弱的数\n" +
                "字化水平，需要云厂商在项目交付、渠道市场、产品完整性等层面持续发力，国内\n" +
                "云市场格局中期仍存在局部调整可能，亦吸引字节等新厂商进入。高速成长的市场、\n" +
                "突出的规模效应、不断优化的产品结构等料将推动一线云厂商强者恒强，建议长期\n" +
                "重点关注阿里云、腾讯云、华为云、百度云等头部厂商。我们亦看好下半年国内云\n" +
                "厂商 capex 预计反转会带来的 IDC、数据中心硬件（量+价）周期性投资机会，以\n" +
                "及北美云厂商 400G 技术周期下capex的高速光模块机会，建议重点关注万国数据、秦淮\n" +
                "数据、数据港、世纪互联、紫光股份、浪潮信息、中际旭创、新易盛等。2021 年 7 月美国非农数据点评—非农如期改善，Taper 渐行渐近宏观经济｜7 月美国非农就业数据如期改善，就业意愿低的问题已出现积极变化，\n" +
                "符合我们预期。Delta 病毒对美国经济和就业的冲击有限，往后失业救济金退坡会\n" +
                "进一步提升就业意愿。未来几个月美国就业有望加速修复，美联储 Taper 预期也将\n" +
                "渐行渐近。11111";
        String pattern = "路径明晰";
        Instant start = Instant.now();
        for (int i = 0; i < 1; i++) {
            int kmp = sundaySearch(target, pattern);
            System.out.println(kmp);
        }
        System.out.println("cost = " + Duration.between(start, Instant.now()).toMillis() + "ms");
    }

    private int sundaySearch(String target, String pattern) {
        char[] charTarget = target.toCharArray();
        char[] charPattern = pattern.toCharArray();

        int t = 0;
        int s = 0;
        while (s < charPattern.length && t < charTarget.length) {
            if (ignoreCaseEquals(charPattern[s], charTarget[t])) {
                if ((s + 1) != charPattern.length) {
                    s++;
                    t++;
                } else {
                    return 1;
                }
            } else {
                int num = t + charPattern.length;
                int index = -1;
                if (num < charTarget.length) {
                    for (int i = 0; i < charPattern.length; i++) {
                        if (ignoreCaseEquals(charPattern[i], charTarget[num])) {
                            index = i;
                            break;
                        }
                    }
                    if (index != -1) {
                        t = t + (charPattern.length - index);
                    } else {
                        t = num + 1;
                    }
                    s = 0;
                } else {
                    break;
                }
            }
            if (t >= charTarget.length) {
                break;
            }
        }
        return -1;
    }

    private boolean ignoreCaseEquals(char c1, char c2) {
        if (c1 == c2) {
            return true;
        }
        if (judgeUpp(c1) && judgeLow(c2)) {
            return Character.toLowerCase(c1) == c2;
        }

        if (judgeLow(c1) && judgeUpp(c2)) {
            return Character.toUpperCase(c1) == c2;
        }

        return false;
    }

    private boolean judgeUpp(char c) {
        return c >= 'A' && c <= 'Z';
    }

    private boolean judgeLow(char c) {
        return c >= 'a' && c <= 'z';
    }

    @Test
    public void testWhile() {
        int index = 2;
        while (true) {
            if (index == 1) {
                break;
            }
            index--;
        }
        System.out.println(index);
    }

    @Test
    public void encoderTest() throws UnsupportedEncodingException {
        byte b = 23;

        int high = (b & 0xf0) >>> 4;
        int low = b & 0x0f;


        System.out.println(high + " - " + low);

    }

    @Test
    public void hutoolMd5() {
        String str = "kiuyui";
        System.out.println(SecureUtil.md5(str));

        System.out.println(String.format("%02d", 1));
        System.out.println(String.format("%03d", 1));

        List<String> list = new ArrayList<>();
        list.add("123");
        list.add("1234");
        System.out.println(list);

        Set<String> set = new HashSet<>();
        set.add("123");
        set.add("1234");
        System.out.println(set);
    }


    // ======================== 微信网页授权-start ======================== //
    private static final String APP_ID = "wx548de4322c0b660d";
    private static final String APP_SECRET = "a292e34ffcc3234ae16f8811567e00c2";


    @Test
    public void testDecoder() throws UnsupportedEncodingException {
        String url = "https://open.weixin.qq.com/connect/oauth2/authorize?appid=wx548de4322c0b660d&redirect_uri=https%3A%2F%2Fshangbao%2E51fzy%2Ecom%2Fenterprise%2Dcode%2Dh5%2Dweb%2Fbond%3FactivityUrlId%3D3&response_type=code&scope=snsapi_base&state=qym188#wechat_redirect";
        System.out.println(URLDecoder.decode(url, StandardCharsets.UTF_8.toString()));
    }

    @Test
    public void testUrlEncode() {
        String redirectUrl = "http://mtest.51ifind.com/enterprise-code-h5-web/bond";
        String redirectUrl1 = "https://mtest.51ifind.com/enterprise-code-web/manage/companyHonor";
        String redirectUrl2 = "http://mtest.51ifind.com/enterprise-code-h5-web/bond?activityUrlId=1";
        URLEncoder urlEncoder = new URLEncoder();
        System.out.println(urlEncoder.encode(redirectUrl2, StandardCharsets.UTF_8));

        String url = "https://api.weixin.qq.com/sns/oauth2/access_token?appid=wx548de4322c0b660d&secret=a292e34ffcc3234ae16f8811567e00c2&code=%s&grant_type=authorization_code";
        String code = "051VHmll2s8GB84Boinl2bRASs1VHmlF";
        HttpRequest get = HttpUtil.createGet(String.format(url, code));
        System.out.println(System.currentTimeMillis());
        HttpResponse execute = get.execute();
        String result = execute.body();
        System.out.println(System.currentTimeMillis());
        System.out.println(result);
    }

    /**
     * 判断用户是否关注了微信公众号
     */
    @Test
    public void judgeFollow() {
        // 1.获取基础access_token
        String accessToken = getAccessToken();

        // 2.获取用户信息
        String url = "https://api.weixin.qq.com/cgi-bin/user/info?access_token=%s&openid=oe7AYt4znN2DzARdvnwH2oOYXvj8&lang=zh_CN";
        url = String.format(url, accessToken);
        System.out.println("judgeFollow >>> url=" + url);
        String result = HttpUtil.createGet(url).execute().body();
        System.out.println(result);
    }

    @Test
    public void sendMessage() {
        // 1.获取基础access_token
        String accessToken = getAccessToken();

        // 发生消息
        // 单人循环发送
//        String url = "https://api.weixin.qq.com/cgi-bin/message/custom/send?access_token=" + accessToken;
        // 群发
        String url = "https://api.weixin.qq.com/cgi-bin/message/mass/send?access_token=" + accessToken;
        System.out.println("sendMessage >>> url=" + url);

        Map<String, Object> message = new LinkedHashMap<>(6);
        // 发送的用户
        Set<String> touser = new HashSet<>();
        touser.add("oe7AYt4znN2DzARdvnwH2oOYXvj8");
        touser.add("oe7AYt1WG08LRHV4MSD8inxx9NPU");
        message.put("touser", touser);

        // 消息类型
        message.put("msgtype", "text");

        // 内容
        Map<String, Object> content = new LinkedHashMap<>();
        content.put("content", "Hello Yi and Guo wsx!");
//        content.put("content", "Hello Yi and Guo hello!");
        message.put("text", content);

//        for (int i = 0; i < 2; i++) {
//            if (i == 0) {
//                message.put("touser", "oe7AYt1WG08LRHV4MSD8inxx9NPU");
//            } else {
//                message.put("touser", "oe7AYt4znN2DzARdvnwH2oOYXvj8");
//            }
//        HttpRequest request = HttpUtil.createPost(url).body(JSONUtils.toJsonString(message));
//        HttpResponse response = request.execute();
//        System.out.println(response.body());
//        }
        HttpRequest request = HttpUtil.createPost(url).body(JSONUtils.toJsonString(message));
        HttpResponse response = request.execute();
        System.out.println(response.body());

    }


    private String getAccessToken() {
        String accessTokenUrl = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s";
        accessTokenUrl = String.format(accessTokenUrl, APP_ID, APP_SECRET);
        String accessTokenBody = HttpUtil.createGet(accessTokenUrl).execute().body();
        String accessToken = JSONUtil.parseObj(accessTokenBody).getStr("access_token");
        System.out.println("getAccessToken >>> accessToken=" + accessToken);
        return accessToken;
    }
    // ======================== 微信网页授权-end ======================== //

    /**
     * AES:https://cloud.tencent.com/developer/article/1650310
     */
    @Test
    public void testAES() throws Exception {
//        String key = "lNQqkL1BfSwt2MKw";
//        System.out.println();
//        key = MD5.create().digestHex(APP_ID);
//        System.out.println(key);
        String content = "oe7AYt4znN2DzARdvnwH2oOYXvj8";
//        AES aes = SecureUtil.aes(key.getBytes());
//        // 加密
//        String e = aes.encryptBase64(content);
//        System.out.println(e);
//
//        // 解密
//        AES aes1 = SecureUtil.aes(key.getBytes());
//        String decryptStr = aes1.decryptStr(e);
//        System.out.println(decryptStr);

        String encrypt = AESUtils.encrypt("qymmqy-+.ieu173*", content);
        System.out.println(encrypt);

        String decrypt = AESUtils.decrypt("qymmqy-+.ieu173*", encrypt);
        System.out.println(decrypt);

    }

    @Test
    public void testSignToken() {
        Map<String, String> map = new HashMap<>(4);
        map.put("token", "1628438399999");
        map.put("appId", "232de3rew2fw2");
        String signToken = getSignToken(map);
        System.out.println(signToken);

        LocalDate localDate = LocalDate.of(2021, 8, 8);
        System.out.println(DateTimeUtils.toLong(localDate, ZoneId.of("Asia/Shanghai")));
        System.out.println(DateTimeUtils.toLong(localDate.atStartOfDay(), ZoneId.of("Asia/Shanghai")));
        System.out.println(DateTimeUtils.toLong(localDate.atTime(LocalTime.MAX), ZoneId.of("Asia/Shanghai")));

    }


    public String getSignToken(Map<String, String> map) {
        String result = "";
        String appKey = "3dwellings4223cqdreally4s";
        try {
            List<Map.Entry<String, String>> infoIds = new ArrayList<>(map.entrySet());
            // 对所有传入参数按照字段名的 ASCII 码从小到大排序（字典序）
            infoIds.sort(Map.Entry.comparingByKey());

            // 构造签名键值对的格式
            StringBuilder sb = new StringBuilder();
            for (Map.Entry<String, String> item : infoIds) {
                String key = item.getKey();
                String val = item.getValue();
                if (StrUtil.isNotBlank(val)) {
                    sb.append(val);
                }
            }
            result = sb.toString() + appKey;
            result = DigestUtil.md5Hex(result);
        } catch (Exception e) {
            return null;
        }

        return result;
    }

    @Test
    public void testListAddAndRemove() {
        String[] i = {"a", "b", "c", "d"};
        final ArrayList<String> asList = new ArrayList(Arrays.asList(i));
        ListIterator<String> listIterator = asList.listIterator();
        while (listIterator.hasNext()) {
            Object next = listIterator.next();
            if ("a".equals(next.toString())) {
                listIterator.add("b");
                listIterator.remove();
            }
        }
        System.out.println(asList);
    }

    static String FILE_DOWNLOAD = "enterprise-code-backstage/attachment";

    /**
     * <ul>
     * <li>matches()方法用来用来判断整个字符串是否匹配正则表达式，只有全部都匹配的时候才会返回true，否则返回false。但如果是部分匹配的话，则会移动匹配的开始的位置；</li>
     * <li>lookingAt()方法部分匹配，总是从第一个字符进行匹配,匹配成功了不再继续匹配，匹配失败了,也不继续匹配；</li>
     * <li>find()方法，部分匹配，从当前位置开始匹配，找到一个匹配的子串，将移动下次匹配的位置；</li>
     * <li>reset()方法，将当前开始匹配的位置重新设置到左边最开始的位置。</li>
     * </ul>
     */
    @Test
    public void testFindOfPattern() {
        String regex = "\\d{3,5}";
        String charSequence = "123-34345-234-00";
        Pattern pattern = Pattern.compile(regex);

        // 虽然匹配失败，但由于charSequence里面的"123"和pattern是匹配的,所以下次的匹配从位置4开始
        Matcher matcher = pattern.matcher(charSequence);
        System.out.println(matcher.matches());
        // 测试匹配位置
        boolean b = matcher.find(); // false
        System.out.println(matcher.start());

        // 使用reset方法重置匹配位置
        matcher.reset();

        // 第一次find匹配
        System.out.println(matcher.find());
        System.out.println(matcher.group() + " --- " + "start: " + matcher.start() + "  end: " + (matcher.end()));
        // 第二次find匹配
        System.out.println(matcher.find());
        System.out.println(matcher.group() + " --- " + "start: " + matcher.start() + "  end: " + (matcher.end()));
        // 第三次find匹配
        System.out.println(matcher.find());
        System.out.println(matcher.group() + " --- " + "start: " + matcher.start() + "  end: " + (matcher.end()));
        // 第四次find匹配
        System.out.println(matcher.find());

        // 第一次lookingAt匹配
        System.out.println(matcher.lookingAt());
        System.out.println(matcher.group() + " --- " + "start: " + matcher.start() + "  end: " + (matcher.end()));
        // 第二次lookingAt匹配
        System.out.println(matcher.lookingAt());
        System.out.println(matcher.group() + " --- " + "start: " + matcher.start() + "  end: " + (matcher.end()));
    }

    /**
     * <ul>
     *     <li>replaceFirst()只替换匹配到的第一个地方；</li>
     *     <li>replaceAll()替换所有匹配到的地方；</li>
     *     <li>appendReplacement()将替换位置处及之前位置处的字符复制到StringBuffer中；</li>
     *     <li>appendTail()将替换位置之后的字符复制到StringBuffer</li>
     * </ul>
     */
    @Test
    public void testReplaceOfPattern() {
        String regex = "cat";
        String charSequence = "fat cat, fat cat, fat cat, I like.";
        Pattern pattern = Pattern.compile(regex);
        Matcher matcher = pattern.matcher(charSequence);

        // replaceFirst()只替换匹配到的第一个地方
        System.out.println(matcher.replaceFirst("dog"));

        // replaceAll
        System.out.println(matcher.replaceAll("dog"));

        //appendReplacement()将替换位置处及之前位置处的字符复制到StringBuffer中；
        //appendTail()将替换位置之后的字符复制到StringBuffer
        pattern = Pattern.compile(regex);
        matcher = pattern.matcher(charSequence);
        StringBuffer sb = new StringBuffer();
        while (matcher.find()) {
            matcher.appendReplacement(sb, "dog");
            System.out.println(sb.toString());
        }
        matcher.appendTail(sb);
        System.out.println(sb.toString());

        System.out.println("\\");
        System.out.println("\\\\");
        System.out.println("\\\\\\");
    }

    @Test
    public void powTest() {
        int n = 6;
        double pow = Math.pow(0.5, n);
        System.out.println(pow);

        System.out.println("NumberUtil.pow(0.5, n).multiply(new BigDecimal(\"20\")) = "
                + NumberUtil.pow(0.5, n).multiply(new BigDecimal("20")));

        System.out.println("Math.round(NumberUtil.pow(0.5, n).multiply(new BigDecimal(\"20\")).doubleValue()) = "
                + Math.round(NumberUtil.pow(0.5, n).multiply(new BigDecimal("20")).doubleValue()));

        String path = "  ss";
        assignment(path);
        System.out.println(path);
    }

    private void assignment(String path) {
        path = "ydj";
    }

    @Test
    public void doubleToStr() {
        double d1 = 1234567.123d;
        double d2 = 123456784434433.1233d;
        double d3 = 12345678933.12333333d;

        System.out.println(d1);
        System.out.println(d2);
        System.out.println(d3);

        System.out.println(NumberUtil.toStr(new BigDecimal("1.2345678443443312E14")));
        System.out.println(new BigDecimal("50123.12E25").toPlainString());
        System.out.println(NumberUtil.toStr(new BigDecimal("12345678443443322.1233222")));
    }

    @Test
    public void isKxjsf() {
        Pattern pattern = Pattern.compile("^[+-]?\\d+\\.?\\d*[Ee][+-]?\\d+$");
        boolean matches = pattern.matcher("50123.12E25").matches();
        if (matches) {
            System.out.println("是");
        } else {
            System.out.println("否");
        }
    }

    @Test
    public void testRefArr() {
        String[] a = new String[2];
        Object[] b = a;

        a[0] = "hi";
        b[1] = Integer.valueOf(42);

        System.out.println(a);
        System.out.println(b);
    }

    @Test
    public void testArrDeque() {
        ArrayDeque<Integer> stack = new ArrayDeque<>();

        stack.add(1);
        stack.add(2);
        stack.add(3);

        Integer peek = stack.peek();
        stack.poll();
        System.out.println(peek);
        System.out.println(stack.peek());

        stack.add(4);

        System.out.println(stack);
    }

    @Test
    public void testArraysClones() {
        int[] arr = {1, 2, 3};
        int[] arrTmp = arr;

        arrTmp[2] = 4;

        System.out.println(arr[2]);

        int[] clone = arr.clone();
        clone[2] = 5;

        System.out.println(arr[2]);
    }
    
    @Test
    public void test_flatmap() {
        Map<String, List<String>> map = new HashMap<>();
//        map.put("1", Lists.newArrayList("1", "2"));
//        map.put("2", Lists.newArrayList("3", "4"));
        List<String> list = map.values().stream().flatMap(Collection::stream).collect(Collectors.toList());
        System.out.println(list);
    }
}
